Java的数据库连接接口:JDBC

JDBC简介

  • 概念

    • Java DataBase Connectivity,Java 数据库连接。

    • JDBC是sun公司定义的一套操作所有关系型数据库的规则,即接口。

    • 各个数据库厂商去编写这套接口的实现类,包装成数据库驱动jar包。通过导入不同数据库厂商编写的数据库驱动jar包,来对JBDC接口进行实现。

快速入门

  • 导入MySQL数据库驱动jar包
    • mysql-connector-java-5.1.37-bin.jar
    • 复制到项目的libs目录下
    • 右键 –> Add As Library
  • 注册驱动 driver
    • “com.mysql.jdbc.Driver”
  • 获取数据库连接对象 Connection
    • 第一个参数:URL(jdbc:mysql://连接IP地址:端口号/数据库名)
    • 第二个参数:数据库账号
    • 第三个参数:数据库密码
  • 定义SQL语句
  • 获取执行SQL语句的对象 Statement
  • 执行SQL语句,接受返回结果
  • 处理结果
  • 释放资源
1
2
3
4
5
6
7
8
9
10
11
12
# 创建数据库
CREATE DATABASE jdbc;
# 使用数据库
USE jdbc;
# 创建表
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT ,
NAME VARCHAR(20),
balance INT
);
# 插入数据
INSERT INTO account VALUES(1,'Cat',1000),(2,'Dog',1000);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//1.导入驱动jar包
//2.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//3.获取数据库连接对象Connection
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root", "root");
//4.定义SQL语句
String s = "UPDATE account SET balance = 1500 WHERE id =1";
//5.获取执行SQL的对象Statement
Statement statement = connection.createStatement();
//6.执行SQL
int count = statement.executeUpdate(s);
//7.处理结果
System.out.println(count);
//8.释放资源
statement.close();
connection.close();

详解对象

DriverManager

  • DriverManager驱动管理对象:用于管理一组JDBC驱动程序的基本服务。
    • 新增的DataSource接口提供了另一种连接到数据源的方法。 使用DataSource对象是连接到数据源的首选方法。

注册驱动

  • 注册驱动:其实就是一个指定数据库驱动jar包的过程。
  • 我们在快速入门的时候,使用的注册驱动方式是加载文件:
    • 加载MySQL数据库驱动jar包中的com.mysql.jdbc.Driver类
      • Class.forName(“com.mysql.jdbc.Driver”);
  • 那么为什么加载com.mysql.jdbc.Driver类就能够注册驱动呢?我们来探究一下。
    • 其实真正注册驱动调用的是DriverManager类中的registerDriver静态方法
      • 注册与给定的驱动程序 DriverManager :static void registerDriver(Driver driver)
    • 而由于com.mysql.jdbc.Driver类在其静态代码块调用了registerDriver静态方法,所以当我们去加载这个类时,它会帮我们自动的注册驱动,简便了我们的操作。
  • MySQL5之后的驱动jar包可以省略注册驱动的步骤,因为它自动加载了com.mysql.jdbc.Driver类。
    1
    2
    3
    4
    5
    6
    7
    8
    /* com.mysql.jdbc.Driver类中的静态代码块 */
    static {
    try {
    java.sql.DriverManager.registerDriver(new Driver());
    } catch (SQLException E) {
    throw new RuntimeException("Can't register driver!");
    }
    }

获取数据库连接

  • static Connection getConnection(String url, String user, String password)
    • url:指定连接的路径
      • 语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
        • 例子:jdbc:mysql://localhost:3306/db3
        • 如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称
    • user:用户名
    • password:密码

      Connection

  • Connection数据库连接对象:与特定数据库的连接。

获取执行SQL语句的对象

  • Statement createStatement() :创建一个 Statement对象,用于将SQL语句发送到数据库。
  • PreparedStatement prepareStatement(String sql) :创建一个 PreparedStatement对象,用于将参数化的SQL语句发送到数据库。

管理事务

  • void setAutoCommit(boolean autoCommit) :(开启事务)将此连接的自动提交模式设置为给定状态。
    • true(自动提交)
    • false(手动提交,开启事务)
  • void commit() :(提交事务)使自上次提交/回滚以来所做的所有更改都将永久性,并释放此 Connection对象当前持有的任何数据库锁。
  • void rollback() :(回滚事务)撤消在当前事务中所做的所有更改,并释放此 Connection对象当前持有的任何数据库锁。

Statement

  • Statement执行静态SQL语句的对象:用于执行静态SQL语句并返回其生成的结果的对象。

执行sql

  • boolean execute(String sql) :可以执行任意的SQL语句
  • int executeUpdate(String sql) :执行DML(insert、update、delete)语句、DDL(create,alter、drop)语句
    • 返回值:影响的行数
    • 通过影响的行数可以判断语句是否执行成功。返回值>0的则执行成功,反之,则失败。
  • ResultSet executeQuery(String sql) :执行DQL(select)语句

练习:增删改

添加记录

注意:添加记录调用的是executeUpdate方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public static void main(String[] args) {

Connection conn = null;
Statement stat = null;

try {
//1.注册驱动 抓取异常
Class.forName("com.mysql.jdbc.Driver");
//2.定义SQL语句
String s = "INSERT INTO account VALUES (NULL,'Rat',2000);";
//3.获取数据库连接对象 Connection
conn = DriverManager.getConnection("jdbc:mysql:///jdbc", "root", "root");
//4.获取执行SQL的对象 Statement
stat = conn.createStatement();
//5.执行SQL语句
int i = stat.executeUpdate(s);
//6.处理结果
if (i>0) {
System.out.println("操作成功");
}else {
System.out.println("操作失败");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
//7.释放资源
/* 因为Statement和Connection已经在trycatch语句外声明了一个NULL值
由于trycatch语句的特性,可能会造成在执行新的赋值语句之前就被中断了运行,所以Statement和Connection可能会是空值。
于是这里需要采用if语句来避免空指针异常 */
if (stat != null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}

修改记录

除了SQL语句,所有的代码都跟添加记录的练习一模一样。
注意:修改记录执行SQL语句的是executeUpdate方法

1
2
3
// .....
String s = "UPDATE account SET balance =1000 WHERE name = 'rat'";
// .....

删除记录

除了SQL语句,所有的代码都跟添加记录的练习一模一样。
注意:删除记录执行SQL语句的是executeUpdate方法

1
2
3
// .....
String s = "DELETE FROM account WHERE name = 'rat'";
// .....

ResultSet

  • ResultSet结果集对象:表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。

封装查询结果

  • boolean next()::游标向下移动一行,并判断当前行是否是最后一行的末尾(是否有数据)。
    • 如果是最后一行的末尾,则返回false;如果不是,则返回true。
  • getTYPE(参数):获取数据
    • TYPE:代表数据类型
      • 如: int getInt() ,String getString()
    • 参数:
      • int:代表列的编号,从1开始
        • 如: getString(1)
      • String:代表列名称。
        • 如: getDouble(“balance”)
  • 使用步骤:
    • 游标向下移动一行
    • 判断是否有数据
    • 获取数据

练习:查询

查询记录

除了SQL语句、执行SQL语句和处理结果不一样,所有的代码都跟添加记录的练习一模一样。另外,你要在trycatch语句外声明一个ResultSet对象为NULL,并在finally语句中释放ResultSet资源。
注意:查询记录执行SQL语句的是executeQuery方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// .....
//2.定义SQL语句
String s = "SELECT * FROM account";
//3.获取数据库连接对象
conn = DriverManager.getConnection("jdbc:mysql:///jdbc", "root", "root");
//4.获取执行SQL的对象 statement
stat = conn.createStatement();
//5.执行SQL,获取结果集
reSet = stat.executeQuery(s);
//6.处理结果
// 1.让游标向下移动一行
// 2.并判断是否是最后一行
while (reSet.next()) {
// 3.获取数据
int anInt = reSet.getInt(1);
String string = reSet.getString(2);
double aDouble = reSet.getDouble("balance");
// 4.输出数据
System.out.println(anInt + "---" + string + "---" + aDouble);
}
// ....

查询记录(实体类)

代码

  • 查询数据库中emp表的数据,并将其封装为Employee对象,然后将其封装进List集合。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# MySQL数据库

-- 创建表
CREATE TABLE emp (
id INT PRIMARY KEY, -- 员工id
ename VARCHAR(50), -- 员工姓名
job_id INT, -- 职务id
mgr INT , -- 上级领导
joindate DATE, -- 入职日期
salary DECIMAL(7,2), -- 工资
bonus DECIMAL(7,2), -- 奖金
dept_id INT -- 所在部门编号
);

-- 添加员工
INSERT INTO emp(id,ename,job_id,mgr,joindate,salary,bonus,dept_id) VALUES
(1001,'孙悟空',4,1004,'2000-12-17','8000.00',NULL,20),
(1002,'卢俊义',3,1006,'2001-02-20','16000.00','3000.00',30),
(1003,'林冲',3,1006,'2001-02-22','12500.00','5000.00',30),
(1004,'唐僧',2,1009,'2001-04-02','29750.00',NULL,20),
(1005,'李逵',4,1006,'2001-09-28','12500.00','14000.00',30),
(1006,'宋江',2,1009,'2001-05-01','28500.00',NULL,30),
(1007,'刘备',2,1009,'2001-09-01','24500.00',NULL,10),
(1008,'猪八戒',4,1004,'2007-04-19','30000.00',NULL,20),
(1009,'罗贯中',1,NULL,'2001-11-17','50000.00',NULL,10),
(1010,'吴用',3,1006,'2001-09-08','15000.00','0.00',30),
(1011,'沙僧',4,1004,'2007-05-23','11000.00',NULL,20),
(1012,'李逵',4,1006,'2001-12-03','9500.00',NULL,30),
(1013,'小白龙',4,1004,'2001-12-03','30000.00',NULL,20),
(1014,'关羽',4,1007,'2002-01-23','13000.00',NULL,10);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 实体类

public class Employee {
// 成员变量(根据数据库的列来设置)
private int id;
private String ename;
private int job_id;
private int mgr;
private Date join_date;
private double salary;
private double bonus;
private int dept_id;
// 无参构造
public Employee() {
}
// 带参构造(按照数据库表中列的顺序)
public Employee(int id, String ename, int job_id, int mgr, Date join_date, double salary, double bonus, int dept_id) {
this.id = id;
this.ename = ename;
this.job_id = job_id;
this.mgr = mgr;
this.join_date = join_date;
this.salary = salary;
this.bonus = bonus;
this.dept_id = dept_id;
}
// toString方法
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", ename='" + ename + '\'' +
", job_id=" + job_id +
", mgr=" + mgr +
", join_date=" + join_date +
", salary=" + salary +
", bonus=" + bonus +
", dept_id=" + dept_id +
'}';
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// 测试类

// mian方法
public static void main(String[] args) {
//调用方法,接收list集合
List<Employee> emps = returnTable();
//遍历list集合
for (Employee record : emps) {
System.out.println(record);
}
}
// 成员方法(返回查询结果)
public static List<Employee> returnTable(){
Connection conn = null;
Statement stat = null;
ResultSet reSet = null;
List<Employee> tableList = new ArrayList<>();
try {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.定义SQL语句
String s = "SELECT * FROM emp";
//3.获取数据库连接对象
conn = DriverManager.getConnection("jdbc:mysql:///jdbc", "root", "root");
//4.获取执行SQL的对象 statement
stat = conn.createStatement();
//5.执行SQL
reSet = stat.executeQuery(s);
//6.处理结果
//6.1 让游标向下移动一行
//6.2 并判断是否是最后一行
while (reSet.next()) {
//6.3 获取数据
int id = reSet.getInt(1);
String ename = reSet.getString(2);
int job_id = reSet.getInt(3);
int mgr = reSet.getInt(4);
Date join_date = reSet.getDate(5);
double salary = reSet.getDouble(6);
double bonus = reSet.getDouble(7);
int dept_id = reSet.getInt(8);
//6.4 创建 Employee对象
Employee emp = new Employee(id,ename,job_id,mgr,join_date,salary,bonus,dept_id);
//6.5 将Employee对象添加进List集合
tableList.add(emp);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
//7.释放资源
/* 避免空指针异常,使用if语句判断 */
if (reSet != null) {
try {
reSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stat != null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//8. 返回List集合
return tableList;
}

比较

将数据库、实体类和测试类进行比较。

1
2
3
4
5
6
7
8
9
10
11
12
13
# MySQL数据库

-- 创建表
CREATE TABLE emp (
id INT PRIMARY KEY, -- 员工id
ename VARCHAR(50), -- 员工姓名
job_id INT, -- 职务id
mgr INT , -- 上级领导
joindate DATE, -- 入职日期
salary DECIMAL(7,2), -- 工资
bonus DECIMAL(7,2), -- 奖金
dept_id INT -- 所在部门编号
);
1
2
3
4
5
6
7
8
9
10
11
// 实体类

// 成员变量
private int id;
private String ename;
private int job_id;
private int mgr;
private Date join_date;
private double salary;
private double bonus;
private int dept_id;
1
2
3
4
5
6
7
8
9
10
// 测试类
//6.3 获取数据
int id = reSet.getInt(1);
String ename = reSet.getString(2);
int job_id = reSet.getInt(3);
int mgr = reSet.getInt(4);
Date join_date = reSet.getDate(5);
double salary = reSet.getDouble(6);
double bonus = reSet.getDouble(7);
int dept_id = reSet.getInt(8);

PreparedStatement

  • PreparedStatement执行动态SQL语句的对象:表示预编译(动态)的SQL语句的对象。

SQL注入问题

  • 指在拼接SQL时,有一些特殊的SQL关键字参与,从而造成了安全性问题。

用户输入前

1
2
// Java语句
String sql = "SELECT * FROM user WHERE username = '"+username+"' AND password = '"+password+"';";

用户输入后, 账户:随便,密码:a’ or ‘a’=’a

1
2
3
4
# SQL语句
SELECT * FROM JDBC_user WHERE username = '随便' AND password ='a' OR 'a'='a';

# 语句永远成立,轻松登录成功。

解决SQL注入问题

  • 使用PreparedStatement对象来解决SQL注入问题。
    • 预编译(动态)的SQL:参数使用?作为占位符
  • 使用步骤:
    1. 导入驱动jar包
    2. 注册驱动
    3. 获取数据库连接对象 Connection
    4. 定义sql
      • 注意:sql的参数使用?作为占位符。
      • 如:select * from user where username = ? and password = ?;
    5. 传递SQL语句,获取执行动态SQL语句的对象 PreparedStatement
    6. 给?占位符赋值:
      • 方法: setXxx(参数1,参数2)
        • 参数1:?占位符的位置编号,从1 开始
        • 参数2:?占位符的值
    7. 执行sql,接受返回结果,不需要传递sql语句
    8. 处理结果
    9. 释放资源

登录案例

  • 需求
    • 用户通过键盘录入用户名和密码;
    • 查询数据库后,判断用户是否登录成功。

使用静态SQL

1
2
3
4
5
6
7
8
# 创建表
CREATE TABLE USER(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(30),
PASSWORD VARCHAR(30)
);
# 插入数据
INSERT INTO USER VALUES(1,'cat',111),(2,'dog',222);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// [main方法]
public static void main(String[] args) {
//用户输入
Scanner scanner = new Scanner(System.in);
System.out.println("请输入账户:");
String username = scanner.next();
System.out.println("请输入密码:");
String password = scanner.next();
//调用login方法
boolean flg = login(username, password);
//输入
if (flg){
System.out.println("登录成功");
}else {
System.out.println("登录失败");
}
}

//[login方法]
public static boolean login(String username, String password) {
//防止输入空值
if (username == null) {
return false;
}
if (password == null) {
return false;
}
//声明变量
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
//获取Connection对象
connection = JDBCUtils.getConnection();
//编写SQL语句
String sql = "SELECT * FROM JDBC_user WHERE username = '"+username+"' AND password = '"+password+"'";
//获取Statement对象
statement = connection.createStatement();
//执行SQL语句,并获取ResultSet对象
resultSet = statement.executeQuery(sql);
//返回
return resultSet.next();
} catch (SQLException e) {
e.printStackTrace();
}
//返回
return false;
}

使用动态SQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//  .....
try {
//获取Connection对象
connection = JDBCUtils.getConnection();
//编写SQL语句
String sql = "SELECT * FROM user WHERE username = ? AND password = ? ";
//传递SQL语句,获取preparedStatement对象
preparedStatement = connection.prepareStatement(sql);
//通过PreparedStatement类的set方法,给?占位符赋值
preparedStatement.setString(1,username);
preparedStatement.setString(2,password);
//通过PreparedStatement类的executeQuery方法,并获取ResultSet对象
resultSet = preparedStatement.executeQuery();//不需要传参
//返回
return resultSet.next();
} catch (SQLException e) {
// .....

JDBC工具类

  • 经过几个JDBC的案例之后,我们不难发现JDBC的编写中存在大量复杂的相同的代码,为了简化书写,我们引入了JDBC工具类:JDBCUtils。
  • 但仅仅使用JDBC工具类,仍然不能使得获取数据库连接对象的代码变得简洁,而我们每次在获取数据库连接对象时,输入的三个参数是很少会变化的。于是,为了更加简化书写,我们引入了JDBC配置文件:jdbc.properties。
  • 为了达到最终的目的,我们需要在JDBC工具类中来编写以下内容:
    • 静态变量
    • 静态代码块
    • 获取数据库连接对象的静态方法
    • 增删改操作释放资源的静态方法
    • 查询操作释放资源的静态方法
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      //J DBC工具类:JDBCUtils

      public class JDBCUtils {
      // [静态变量]
      private static String url;
      private static String user;
      private static String password;
      private static String driver;

      // [静态代码块:读取配置文件的数据]
      static {
      try {
      // 1.创建properties对象
      Properties properties = new Properties();
      // 1.1 获取 字节码文件
      Class jdbcClass = JDBCUtils.class;
      // 1.2 获取 类加载器
      ClassLoader classLoader = jdbcClass.getClassLoader();
      // 1.3 获取 统一资源定位符
      URL resource = classLoader.getResource("jdbc.properties");
      // 1.4 获取 路径
      String path = resource.getPath();
      // 2.加载配置文件
      properties.load(new FileReader(path));
      // 3.获取数据
      url = properties.getProperty("url");
      user = properties.getProperty("user");
      password = properties.getProperty("password");
      driver = properties.getProperty("driver");
      // 4.注册驱动
      Class.forName(driver);
      } catch (IOException e) {
      e.printStackTrace();
      } catch (ClassNotFoundException e) {
      e.printStackTrace();
      }
      }

      //[成员方法:获取Connection对象]
      public static Connection getConnection() throws SQLException {
      return DriverManager.getConnection(url,user,password);
      }

      //[成员方法:增删改操作释放Connection对象、Statement对象]
      public static void close(Statement stat, Connection conn) {
      if (stat != null) {
      try {
      stat.close();
      } catch (SQLException e) {
      e.printStackTrace();
      }
      }
      if (conn != null) {
      try {
      conn.close();
      } catch (SQLException e) {
      e.printStackTrace();
      }
      }
      }
      //[成员方法:查询操作释放Connection对象、Statement对象、ResultSet对象]
      public static void close(Statement stat, Connection conn, ResultSet reSet) {
      if (reSet != null) {
      try {
      reSet.close();
      } catch (SQLException e) {
      e.printStackTrace();
      }
      }
      if (stat != null) {
      try {
      stat.close();
      } catch (SQLException e) {
      e.printStackTrace();
      }
      }
      if (conn != null) {
      try {
      conn.close();
      } catch (SQLException e) {
      e.printStackTrace();
      }
      }
      }
      }
1
2
3
4
5
# properties提示配置文件
url = jdbc:mysql:///jdbc
user = root
password = root
driver = com.mysql.jdbc.Driver

查询练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 使用JDBC工具类
public static void main(String[] args) {
Connection conn = null;
Statement stat = null;
ResultSet reSet = null;
try {
//1.注册驱动
//2.获取数据库连接对象
conn = JDBCUtils.getConnection();
//3.定义SQL语句
String s = "SELECT * FROM account";
//4.获取执行SQL的对象 statement
stat = conn.createStatement();
//5.执行SQL
reSet = stat.executeQuery(s);
//6.处理结果
//1.让游标向下移动一行
//2.并判断是否是最后一行
while (reSet.next()) {
//3.获取数据
int anInt = reSet.getInt(1);
String string = reSet.getString(2);
double aDouble = reSet.getDouble(3);
//4.输出数据
System.out.println(anInt + "---" + string + "---" + aDouble);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
//7.释放资源
JDBCUtils.close(stat,conn,reSet);
}
}

JDBC控制事务

  • 事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。

  • JDBC使用Connection对象来管理事务

    • 开启事务:setAutoCommit(boolean autoCommit) (false为开启事务)

      • 在执行sql之前开启事务
    • 提交事务:commit()

      • 当所有sql都执行完提交事务
    • 回滚事务:rollback()

      • 在catch中回滚事务
  • 我在代码中写入了一句异常 ,当程序运行到异常时,异常后的提交事务不会进行,程序运行catch语句,进行事务回滚,账户会回滚到之前的状态。当然,如果去除异常,交易会正常进行,账户会发生变化。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public static void main(String[] args) {

Connection connection = null;
PreparedStatement preparedStatement = null;

try {
//获取Connection对象
connection = JDBCUtils.getConnection();
//【开启事务】
connection.setAutoCommit(false);
//定义SQL语句 [Cat -500]***
String sql1 = "UPDATE account SET balance = balance - ? WHERE id = ?";
//传递SQL语句,获取preparedStatement对象
preparedStatement = connection.prepareStatement(sql1);
//通过PreparedStatement类的set方法,给?赋值
preparedStatement.setInt(1,500);
preparedStatement.setInt(2,1);
//执行SQL语句
int i = preparedStatement.executeUpdate();
System.out.println(i);
//定义SQL语句 [Dog +500]***
String sql2 = "UPDATE account SET balance = balance + ? WHERE id = ?";
//传递SQL语句,获取preparedStatement对象
preparedStatement = connection.prepareStatement(sql2);
//通过PreparedStatement类的set方法,给?赋值
preparedStatement.setInt(1,500);
preparedStatement.setInt(2,2);
//执行SQL语句
i = preparedStatement.executeUpdate();
System.out.println(i);
// ***异常***
int iii = 3/0;
//【提交事务】
connection.commit();
} catch (Exception e) {
//【事务回滚】
try {
if (connection!= null) {
connection.rollback();
}
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
} finally {
// 释放资源
JDBCUtils.close(preparedStatement,connection);
}
}
-------------本文结束-------------
Donate comment here